package org.ncibi.cytoscape.metscape.network;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.ncibi.cytoscape.metscape.data.Attributes;
import org.ncibi.cytoscape.metscape.data.Concept;
import org.ncibi.cytoscape.metscape.data.ConceptData;
import org.ncibi.cytoscape.metscape.data.Organism;
import org.ncibi.cytoscape.metscape.plugin.MetScapePlugin;
import org.ncibi.metab.network.MetabolicNetwork;
import org.ncibi.metab.network.edge.MetabolicEdge;
import org.ncibi.metab.network.node.MetabolicNode;

import cytoscape.CyEdge;
import cytoscape.CyNetwork;
import cytoscape.CyNode;
import cytoscape.Cytoscape;
import cytoscape.data.CyAttributesUtils;
import cytoscape.groups.CyGroup;
import cytoscape.groups.CyGroupManager;

public class ExpandInSubnetworkTranslator extends DefaultNetworkTranslator {

	private CyNetwork network;
	private String cid;

	public ExpandInSubnetworkTranslator(String cid, String networkName, MetabolicNetwork sourceNetwork, CyNetwork network){
		super(networkName, sourceNetwork, null, null);
		this.network = network;
		this.cid = cid;
		this.compoundData = MetScapePlugin.getPluginData().getCompoundDataStore()
			.get(Attributes.network.getIntegerAttribute(network.getIdentifier(), "CompoundData"));
		this.geneData = MetScapePlugin.getPluginData().getGeneDataStore()
			.get(Attributes.network.getIntegerAttribute(network.getIdentifier(), "GeneData"));
		this.organism = Organism.toOrganism(Attributes.network.getIntegerAttribute(network.getIdentifier(), "Taxid"));
		this.compoundNodeTranslator = new CompoundNodeTranslator(compoundData);
		this.geneNodeTranslator = new GeneNodeTranslator(geneData,organism);
	}

	@Override
	public CyNetwork toCytoscapeNetwork() {

		List<CyNode> nodesList = new ArrayList<CyNode>();
		List<CyEdge> edgesList = new ArrayList<CyEdge>();

		for (MetabolicNode node : sourceNetwork.getAllNodes())
		{
			CyNode cyNode = getTranslatorForNode(node).toCyNode(node);
			String category = Attributes.node.getStringAttribute
				(cyNode.getIdentifier(), "Category." + network.getTitle());
			if(category == null)
				category = Attributes.node.getStringAttribute
					(cyNode.getIdentifier(), "Type");
			
			Attributes.node.setAttribute
				(cyNode.getIdentifier(), "Category." + networkName, category);
			Attributes.node.setAttribute(cyNode.getIdentifier(), "isExpansion." + networkName, false);
            Attributes.node.setAttribute(cyNode.getIdentifier(), "isExpansionSeed." + networkName, false);
            Attributes.node.setAttribute(cyNode.getIdentifier(), "isSubnetworkExpansionSeed." + networkName, false);
			nodesList.add(cyNode);
		}

		for (MetabolicEdge edge : sourceNetwork.getEdges())
		{
			CyEdge cyEdge = getTranslatorForEdge(edge).toCyEdge(edge);
			Attributes.edge.setAttribute(cyEdge.getIdentifier(), "isExpansion." + networkName, false);
			edgesList.add(cyEdge);
		}
		String compoundDataName = compoundData.getName();
		String compoundPvalueColumn = Attributes.network.getStringAttribute(network.getIdentifier(), "CompoundPvalueColumn");
		Double compoundPvalueThreshold = Attributes.network.getDoubleAttribute(network.getIdentifier(), "CompoundPvalueThreshold");
		String compoundFoldChangeColumn = Attributes.network.getStringAttribute(network.getIdentifier(), "CompoundFoldChangeColumn");
		Double compoundFoldChangeThreshold = Attributes.network.getDoubleAttribute(network.getIdentifier(), "CompoundFoldChangeThreshold");
		
		String geneDataName = geneData.getName();
		String genePvalueColumn = Attributes.network.getStringAttribute(network.getIdentifier(), "GenePvalueColumn");
		Double genePvalueThreshold = Attributes.network.getDoubleAttribute(network.getIdentifier(), "GenePvalueThreshold");
		String geneFoldChangeColumn = Attributes.network.getStringAttribute(network.getIdentifier(), "GeneFoldChangeColumn");
		Double geneFoldChangeThreshold = Attributes.network.getDoubleAttribute(network.getIdentifier(), "GeneFoldChangeThreshold");
		
		Map<?,?> compoundPvalues =
			CyAttributesUtils.getAttribute("Compound." + compoundDataName + "." + compoundPvalueColumn, Cytoscape.getNodeAttributes());
		if(compoundPvalues != null)
		for(Object id: compoundPvalues.keySet()) {
			if(compoundPvalueThreshold == null || (Double) compoundPvalues.get(id) <= compoundPvalueThreshold) {
				Attributes.node.setAttribute(id.toString(),"isSignificant."+networkName,true);
			}
			else {
				Attributes.node.setAttribute(id.toString(),"isSignificant."+networkName,false);
			}
		}
		else {
			Map<?,?> hasData = CyAttributesUtils.getAttribute
				("Compound." + compoundDataName + ".hasData", Cytoscape.getNodeAttributes());
			if(hasData != null)
			for(Object id: hasData.keySet()) {
				if(hasData.get(id).equals(true))
					Attributes.node.setAttribute(id.toString(), "isSignificant."+networkName, true);
			}
		}
		
		Map<?,?> compoundFoldChanges =
			CyAttributesUtils.getAttribute("Compound." + compoundDataName + "." + compoundFoldChangeColumn, Cytoscape.getNodeAttributes());
		if(compoundFoldChanges != null) {
			Double upThreshold = 1.0;
			Double downThreshold = 1.0;
			if(compoundData.getColumnIsSigned().get(compoundFoldChangeColumn)) {
				upThreshold = Math.abs(compoundFoldChangeThreshold);
				downThreshold = -Math.abs(compoundFoldChangeThreshold);
			}
			else {
				if(compoundFoldChangeThreshold >= 1) {
					upThreshold = compoundFoldChangeThreshold;
					downThreshold = 1/compoundFoldChangeThreshold;
				}
				else if(compoundFoldChangeThreshold > 0) {
					upThreshold = 1/compoundFoldChangeThreshold;
					downThreshold = compoundFoldChangeThreshold;
				}
			}
			for(Object id: compoundFoldChanges.keySet()) {
				if((Double) compoundFoldChanges.get(id) >= upThreshold) {
					Attributes.node.setAttribute(id.toString(),"direction."+networkName,"up");
				}
				else if((Double) compoundFoldChanges.get(id) <= downThreshold) {
					Attributes.node.setAttribute(id.toString(),"direction."+networkName,"down");
				}
			}
		}
		
		Map<?,?> genePvalues =
			CyAttributesUtils.getAttribute("Gene." + geneDataName + "." + genePvalueColumn, Cytoscape.getNodeAttributes());
		if(genePvalues != null)
		for(Object id: genePvalues.keySet()) {
			if(genePvalueThreshold == null || (Double) genePvalues.get(id) <= genePvalueThreshold) {
				Attributes.node.setAttribute(id.toString(),"isSignificant."+networkName,true);
			}
			else {
				Attributes.node.setAttribute(id.toString(),"isSignificant."+networkName,false);
			}
		}
		else {
			Map<?,?> hasData = CyAttributesUtils.getAttribute
				("Gene." + geneDataName + ".hasData", Cytoscape.getNodeAttributes());
			if(hasData != null)
			for(Object id: hasData.keySet()) {
				if(hasData.get(id).equals(true))
					Attributes.node.setAttribute(id.toString(),"isSignificant."+networkName,true);
			}
		}
		
		Map<?,?> geneFoldChanges =
			CyAttributesUtils.getAttribute("Gene." + geneDataName + "." + geneFoldChangeColumn, Cytoscape.getNodeAttributes());
		if(geneFoldChanges != null) {
			Double upThreshold = 1.0;
			Double downThreshold = 1.0;
			if(geneData.getColumnIsSigned().get(geneFoldChangeColumn)) {
				upThreshold = Math.abs(geneFoldChangeThreshold);
				downThreshold = -Math.abs(geneFoldChangeThreshold);
			}
			else {
				if(geneFoldChangeThreshold >= 1) {
					upThreshold = geneFoldChangeThreshold;
					downThreshold = 1/geneFoldChangeThreshold;
				}
				else if(geneFoldChangeThreshold > 0) {
					upThreshold = 1/geneFoldChangeThreshold;
					downThreshold = geneFoldChangeThreshold;
				}
			}
			for(Object id: geneFoldChanges.keySet()) {
				if((Double) geneFoldChanges.get(id) >= upThreshold) {
					Attributes.node.setAttribute(id.toString(),"direction."+networkName,"up");
				}
				else if((Double) geneFoldChanges.get(id) <= downThreshold) {
					Attributes.node.setAttribute(id.toString(),"direction."+networkName,"down");
				}
			}
		}
		
		ConceptData conceptData = MetScapePlugin.getPluginData().getConceptDataStore()
			.get(Attributes.network.getIntegerAttribute(network.getIdentifier(), "ConceptData"));
		CyNetwork subnetwork = Cytoscape.createNetwork(nodesList, edgesList, networkName, network);
		for(Object node: subnetwork.nodesList()) {
			CyNode cyNode = (CyNode) node;
			List<Concept> concepts = conceptData.getConceptMap().get(cyNode.getIdentifier());
        	if(concepts != null)
            for(Concept concept: concepts) {
            	CyGroup group = CyGroupManager.findGroup(subnetwork.getIdentifier() + "-" + concept.getConceptName());
            	if(group == null) {
            		group = CyGroupManager.createGroup
            			(subnetwork.getIdentifier() + "-" + concept.getConceptName(), null, subnetwork);
            		CyNode groupNode = group.getGroupNode();
            		Attributes.node.setAttribute(groupNode.getIdentifier(), "Concept.name", concept.getConceptName());
            		Attributes.node.setAttribute(groupNode.getIdentifier(), "Concept.numUniqueGenes", concept.getNumUniqueGenes());
            		Attributes.node.setAttribute(groupNode.getIdentifier(), "Concept.numGenesInNetwork", 0);
            		Attributes.node.setAttribute(groupNode.getIdentifier(), "Concept.pvalue", concept.getPvalue());
            		Attributes.node.setAttribute(groupNode.getIdentifier(), "Concept.fdr", concept.getFdr());
            		Attributes.node.setAttribute(groupNode.getIdentifier(), "Concept.direction", concept.getDirection());
            	}
            	if(Attributes.node.getStringAttribute(cyNode.getIdentifier(), "Type").equals("Gene")) {
            		CyNode groupNode = group.getGroupNode();
            		Integer numGenesInNetwork = Attributes.node.getIntegerAttribute(groupNode.getIdentifier(), "Concept.numGenesInNetwork");
            		Attributes.node.setAttribute(groupNode.getIdentifier(), "Concept.numGenesInNetwork", ++numGenesInNetwork);
            	}
            	group.addNode(cyNode);
            }
		}
		Attributes.node.setAttribute(cid, "isExpansionSeed." + networkName, true);
		Attributes.node.setAttribute(cid, "isSubnetworkExpansionSeed." + networkName, true);
		CyAttributesUtils.copyAttributes(network.getIdentifier(), subnetwork.getIdentifier(),
				Cytoscape.getNetworkAttributes(), false);

		return subnetwork;
	}
}
